package com.fnic.pearl.scheduler.action;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.fnic.pearl.scheduler.SchedulerConfig;
import com.fnic.pearl.scheduler.constant.CondHeaderField;
import com.fnic.pearl.scheduler.constant.PearlErrorCode;
import com.fnic.pearl.scheduler.constant.TestTaskStatus;
import com.fnic.pearl.scheduler.dao.SchedulerStore;
import com.fnic.pearl.scheduler.mod.ProbeStatusManager;
import com.fnic.pearl.scheduler.mod.TestTaskSchedulerRuler;
import com.fnic.pearl.scheduler.model.HeartBeatResp;
import com.fnic.pearl.scheduler.model.ProbeStatus;
import com.fnic.pearl.scheduler.model.ProbeTaskStatus;
import com.fnic.pearl.scheduler.model.TestTask;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * 处理探针的心跳消息
 * 
 * @author HuHaiyang
 * @date 2013年7月14日
 */
public class ProbeHBServlet extends HttpServlet
{

    private static final long serialVersionUID = 1L;

    private static Logger LOG = Logger.getLogger(ProbeHBServlet.class);

    private Gson gsonOldTask = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
            .setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        int probeId = 0;
        String[] values = request.getParameterValues(CondHeaderField.COND_PROBE_ID);
        if (values == null || values.length != 1)
        {
            LOG.warn("doPost - no param " + CondHeaderField.COND_PROBE_ID);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        try
        {
            probeId = Integer.valueOf(values[0]);
        }
        catch (NumberFormatException e)
        {
            LOG.warn("probeId format error: " + e.getMessage());
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        handleProbeIdHB(request, response, probeId);
    }

    /**
     * 处理以 探针ID 为标识的节点心跳
     * 
     * @param request 接收的 HTTP 请求
     * @param response 返回的 HTTP 响应
     * @param probeId 探针节点ID
     * @throws IOException io exception
     */
    private void handleProbeIdHB(HttpServletRequest request, HttpServletResponse response, int probeId)
            throws IOException
    {
        ProbeStatus ps = ProbeStatusManager.getInstance().get(probeId);
        // begin mod by yyzhang 20140819
        if (ps == null || ps.getStatus() != ProbeStatus.S_LOGIN)
        {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        // end mod by yyzhang 20140819
        ps.setHandlingHB(true);
        ps.setLastRecvHBTime(new Date());

        // ---- 优先返回 非测量任务消息 ----
        HeartBeatResp hbr = SchedulerStore.getInstance().popHbResp(ps.getProbeId());
        if (hbr != null)
        {
            response.setHeader(CondHeaderField.HB_BODY_TYPE, hbr.getHbType());
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            LOG.info("send non HB - " + hbr);
            response.getWriter().write(hbr.getContent());
            response.getWriter().flush();

            ps.setHandlingHB(false);
            ps.setLastHandleHBTime(new Date());
            return;
        }

        // ---- 待下发的测量任务 ----
        List<TestTask> tasks = SchedulerStore.getInstance().popWissueTestTask(probeId,
                SchedulerConfig.getInstance().getMaxTestTaskNum(), SchedulerConfig.getInstance().getHbTimeout());
        if (tasks == null || tasks.size() == 0)
        {
            response.setStatus(HttpServletResponse.SC_OK);
            ps.setHandlingHB(false);
            ps.setLastHandleHBTime(new Date());
            return;
        }

        ListIterator<TestTask> iterTask = tasks.listIterator();
        while (iterTask.hasNext())
        {
            TestTask tt = iterTask.next();

            // 在下发前再判断一次是否为过期任务
            // 主要是因为放到 wissue 队列和能否下发之间有时间差
            // (依赖于 probe 什么时候发心跳过来)
            if (TestTaskSchedulerRuler.isExpire(tt))
            {
                tasks.remove(tt);
                SchedulerStore.getInstance().updateTestTaskStatus(tt, TestTaskStatus.S_FAILED_TASK_ISSUE);
                SchedulerStore.getInstance().putSFHTestTask(tt);

                ProbeTaskStatus pts = new ProbeTaskStatus(tt.getProbe(), tt.getTaskId(),
                        TestTaskStatus.S_FAILED_TASK_ISSUE);
                pts.setExecNum(tt.getExecNum());
                pts.setErrorNo(PearlErrorCode.SCHD_ERR_SVC_INVALID_END_TIME);;
                SchedulerStore.getInstance().putTestTaskStatus(pts);
            }
        }

        // boolean isEncrypt = SchedulerConfig.getInstance().isEncryptTestTask();
        // if (isEncrypt)
        // {
        // byte[] encryptedBody = null;
        // try
        // {
        // encryptedBody = AesCipherUtil.encrypt(gsonOldTask.toJson(tasks), "1234567890123456");
        // }
        // catch (Exception e)
        // {
        // LOG.warn("encrypt failed - " + e.getMessage());
        // response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        // ps.setHandlingHB(false);
        // ps.setLastHandleHBTime(new Date());
        // return;
        // }
        //
        // response.setIntHeader(CondHeaderField.BODY_ENCRYPT, 1);
        // response.setHeader(CondHeaderField.HB_BODY_TYPE, CondHeaderField.HB_BTYPE_TESTASK);
        //
        // response.setContentType("application/json;charset=utf-8");
        // response.setStatus(HttpServletResponse.SC_OK);
        //
        // response.getOutputStream().write(encryptedBody);
        // response.getOutputStream().flush();
        // }
        // else
        // {
        response.setHeader(CondHeaderField.HB_BODY_TYPE, CondHeaderField.HB_BTYPE_TESTASK);

        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);

        String jsonTasks = gsonOldTask.toJson(tasks);
        response.getWriter().write(jsonTasks);
        response.getWriter().flush();
        if (response.getWriter().checkError())
        {
            LOG.warn("issue test task failed!!! - " + jsonTasks);

            // 重新放入队列
            for (int idx = tasks.size() - 1; idx >= 0; --idx)
            {
                SchedulerStore.getInstance().putHeadWissueTestTask(tasks.get(idx));
            }

            ps.setHandlingHB(false);
            ps.setLastHandleHBTime(new Date());
            return;
        }
        // }

        // 添加至 issued 队列
        for (TestTask tt : tasks)
        {
            tt.setStatus(TestTaskStatus.S_ISSUED);
            SchedulerStore.getInstance().putIssuedTestTask(tt);

            ProbeTaskStatus pts = new ProbeTaskStatus(tt.getProbe(), tt.getTaskId(), TestTaskStatus.S_ISSUED);
            pts.setExecNum(tt.getExecNum());
            SchedulerStore.getInstance().putTestTaskStatus(pts);
        }

        ps.setHandlingHB(false);
        ps.setLastHandleHBTime(new Date());
    }
}
